package org.example.aqs;

import lombok.extern.slf4j.Slf4j;
import org.example.utils.Sleeper;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * @author zhengdp
 * @Description 自定义阻塞式锁 （不可重入锁）
 * @Date 2022/12/27 13:40
 * @Created by zhengdp
 */
@Slf4j(topic = "c.TestAqs")
public class TestAqs {
    public static void main(String[] args) {
        MyLock lock = new MyLock();
        new Thread(() -> {
            lock.lock();
            try {
                log.info("locking ...");
                Sleeper.sleep(1000);
            }finally {
                log.info("unlocking ...");
                lock.unlock();
            }
        },"t1").start();

        new Thread(() -> {
            lock.lock();
            try {
                log.info("locking ...");
            }finally {
                log.info("unlocking ...");
                lock.unlock();
            }
        },"t2").start();
    }
}

/**
 * 自定义锁 （不可重入锁）
 * 自定义锁通过调用同步器类提供的方法来实现自己的逻辑
 */
class MyLock implements Lock {

    /**
     * 同步器类 (独占锁）
     * state : 0  --> 未加锁
     * state : 1 --> 已加锁
     */
    static class MySync extends AbstractQueuedSynchronizer {
        /**
         * 尝试获取锁
         * state : 0 --> 1 ==> 加锁成功
         * @param arg the acquire argument. This value is always the one
         *        passed to an acquire method, or is the value saved on entry
         *        to a condition wait.  The value is otherwise uninterpreted
         *        and can represent anything you like.
         * @return
         */
        @Override
        protected boolean tryAcquire(int arg) {
            if(compareAndSetState(0,1)){
                // 加锁成功,设置owner线程（持有者线程）
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }else{
                return false;
            }
        }

        /**
         * 如果线程是当前锁的持有者，直接将 state 从 1 --> 0 即可，不需要compareSet
         * (因为是AQS实现的是阻塞式锁，所以只有线程的持有者才会去调用到tryRelease()方法，
         * 因此不会有线程来进行竞争，所以可以直接将 state 从 1 设置到 0 )
         * @param arg the release argument. This value is always the one
         *        passed to a release method, or the current state value upon
         *        entry to a condition wait.  The value is otherwise
         *        uninterpreted and can represent anything you like.
         * @return
         */
        @Override
        protected boolean tryRelease(int arg) {
            setExclusiveOwnerThread(null);
            // state 为 volatile 变量，设置该变量，加入写屏障，保证 exclusiveOwnerThread的修改会被更新到主存中
            setState(0);
            return true;
        }

        /**
         * 是否持有独占锁 (判断当前线程是否持有独占锁）
         * @return
         */
        @Override
        protected boolean isHeldExclusively() {
            return getState() == 1 && Thread.currentThread() == getExclusiveOwnerThread();
        }

        public Condition newCondition(){
            return new ConditionObject();
        }
    }

    private MySync sync = new MySync();

    /**
     * 加锁，不可打断
     * 不成功，进入等待队列等待
     */
    @Override
    public void lock() {
        // 同步器的基类，在该方法中，会调用 子类的tryAcquire方法来尝试加锁，如果加锁失败，会进入等待队列
        sync.acquire(1);
    }

    /**
     * 加锁，可打断（可由其他线程打断）
     * @throws InterruptedException
     */
    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    /**
     * 尝试加锁
     * @return 加锁结果
     */
    @Override
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }

    /**
     * 尝试加锁，带超时时间的尝试加锁
     * @param time the maximum time to wait for the lock
     * @param unit the time unit of the {@code time} argument
     * @return
     * @throws InterruptedException
     */
    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1,unit.toNanos(time));
    }

    /**
     * 解锁
     */
    @Override
    public void unlock() {
        // unparkSuccessor(h) 唤醒正在阻塞的线程
        sync.release(0);
    }

    /**
     * 创建条件变量
     * @return
     */
    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }
}
